import { Controller, Get, OnModuleInit } from '@nestjs/common';
import { HelloServiceClient } from './hello.type';
import { Client, ClientGrpc, Transport } from '@nestjs/microservices';
import { join } from 'path';
import * as grpc from '@grpc/grpc-js';

@Controller('hello')
export class HelloClientController implements OnModuleInit {
  private helloServiceClient: HelloServiceClient;

  // grpc 客户端注册方式之一，首先方式，结合在 hero.module.ts 文件中 ClientModule.register() 注入
  // constructor(@Inject(HERO_PACKAGE) private readonly client: ClientGrpc) {}

  // grpc 客户端注册方式之二：@Client
  @Client({
    transport: Transport.GRPC,
    options: {
      package: 'hello',
      protoPath: join(__dirname, './hello.proto'),
    },
  })
  private client: ClientGrpc;

  onModuleInit() {
    this.helloServiceClient =
      this.client.getClientByServiceName('HelloService');
  }

  @Get('/bidi')
  bidiHello() {
    return new Promise((resolve) => {
      // request header 发送
      const requestHeaderMetadata = new grpc.Metadata();
      requestHeaderMetadata.set('timestamp', new Date().toISOString());

      const call = this.helloServiceClient.bidiHello(requestHeaderMetadata, {
        interceptors: [
          (options, nextCall) => {
            console.log('CALLED CLIENT INTERCEPTOR');
            return new grpc.InterceptingCall(nextCall(options));
          },
        ],
      });

      // response header 接收
      call.on('metadata', (metadata) => {
        const timestamps = metadata.get('timestamp');

        if (timestamps.length > 0) {
          console.log('client timestamp from header:');
          for (const [index, value] of timestamps.entries()) {
            console.log(` ${index}. ${value}`);
          }
        } else {
          console.error("timestamp expected but doesn't exist in header");
        }
      });

      call.on('data', (message) => {
        console.log(
          '🚀 ~ HelloClientController ~ bidiHello ~ message:',
          JSON.stringify(message),
        );
      });

      // response trailer 接收
      call.on('status', (status) => {
        const timestamps = status.metadata.get('timestamp');

        if (timestamps.length > 0) {
          console.log('timestamp from trailer:');
          for (const [index, value] of timestamps.entries()) {
            console.log(` ${index}. ${value}`);
          }
        } else {
          console.error("timestamp expected but doesn't exist in trailer");
        }
      });

      call.on('end', () => {
        console.log('Duplex Streaming end ');
        resolve({ status: 'success', data: null });
      });

      const hello = 'Hello From Duplex';

      for (let i = 0; i < hello.length; i++) {
        call.write({ greeting: hello[i] });
      }
      call.end();
    });
  }

  @Get('/client-stream')
  lotsOfGreeting() {
    return new Promise((resolve, reject) => {
      // request header 发送
      const requestHeaderMetadata = new grpc.Metadata();
      requestHeaderMetadata.set('timestamp', new Date().toISOString());

      const call = this.helloServiceClient.LotsOfGreetings(
        requestHeaderMetadata,
        {
          interceptors: [
            (options, nextCall) => {
              console.log('CALLED CLIENT INTERCEPTOR');
              return new grpc.InterceptingCall(nextCall(options));
            },
          ],
        },
        (err, data) => {
          if (err) {
            console.error(err);
            reject(err);
          } else {
            console.log(
              '🚀 ~ HelloClientController ~ call ~ data:',
              JSON.stringify(data),
            );
            resolve({ status: 'success', data });
          }
        },
      );

      // response header 接收
      call.on('metadata', (metadata) => {
        const timestamps = metadata.get('timestamp');

        if (timestamps.length > 0) {
          console.log('timestamp from header:');
          for (const [index, value] of timestamps.entries()) {
            console.log(` ${index}. ${value}`);
          }
        } else {
          console.error("timestamp expected but doesn't exist in header");
        }
      });

      // response trailer 接收
      call.on('status', (status) => {
        const timestamps = status.metadata.get('timestamp');

        if (timestamps.length > 0) {
          console.log('timestamp from trailer:');
          for (const [index, value] of timestamps.entries()) {
            console.log(` ${index}. ${value}`);
          }
        } else {
          console.error("timestamp expected but doesn't exist in trailer");
        }
      });

      const params = 'Client Stream';

      for (let i = 0; i < params.length; i++) {
        call.write({ greeting: params[i] });
      }
      call.end();
    });
  }
}
